package models

import (
	"errors"
	"fmt"
	"sort"
	"strings"
	"time"

	"gitee.com/sansaniot/ssiot-core/facade/env"
	"gitee.com/sansaniot/ssiot-core/httpmvc/model"
	"gitee.com/sansaniot/ssiot-core/logger"
	"gorm.io/datatypes"
	"gorm.io/gorm"

	"ssdevice/common/constant"
	cDto "ssdevice/common/dto"
	"ssdevice/common/enums"
	"ssdevice/common/utils"
	"ssdevice/internal/device/models/query"
)

type SysDevSensor struct {
	Id           string          `json:"id" gorm:"primaryKey;type:varchar(64);comment:id"`
	Sn           string          `json:"sn" gorm:"type:varchar(255);comment:设备序列号"`
	ProductModel string          `json:"productModel" gorm:"type:varchar(255);comment:产品编码"`
	Key          string          `json:"key" gorm:"type:varchar(255);comment:变量二级key"`
	Label        string          `json:"label" gorm:"type:varchar(255);comment:变量名称"`
	Type         string          `json:"type" gorm:"type:varchar(32);comment:变量类型"`
	Data         datatypes.JSON  `json:"data" gorm:"type:jsonb;comment:变量值"`
	Index        *int            `json:"index" gorm:"type:int2;comment:变量序号"`
	Schema       datatypes.JSON  `json:"schema" gorm:"type:jsonb;comment:二级变量schema描述"`
	Config       datatypes.JSON  `json:"config" gorm:"type:jsonb;comment:二级变量配置"`
	Param        datatypes.JSON  `json:"param" gorm:"type:jsonb;comment:变量扩展信息"`
	CreateTime   *cDto.LocalTime `json:"createTime" gorm:"type:timestamptz(6);comment:创建时间"`
	UpdateTime   *cDto.LocalTime `json:"updateTime" gorm:"type:timestamptz(6);comment:更新时间"`
	model.Model

	DevName      string `json:"devName" gorm:"-"`
	DevType      string `json:"devType" gorm:"-"`
	ProductName  string `json:"productName" gorm:"-"`
	DeptId       string `json:"deptId" gorm:"-"`
	DeptName     string `json:"deptName" gorm:"-"`
	OnlineStatus string `json:"onlineStatus" gorm:"-"`
	AlarmStatus  bool   `json:"alarmStatus" gorm:"-"`
	TimeInsStr   string `gorm:"-"`
}

func (SysDevSensor) TableName() string {
	return "sys_dev_sensor"
}

func (e *SysDevSensor) Generate() interface{} {
	o := *e
	return &o
}

func (e *SysDevSensor) FindDevSensor(req *query.SysDevSensorQuery) (err error, one SysDevSensor) {
	err = e.FindOne(&one, cDto.MakeCondition(req.GetNeedSearch()))
	return
}

func (e *SysDevSensor) FindSensorList(req *query.SysDevSensorQuery) (err error, result interface{}) {
	if len(req.ProductModel) > 0 || len(req.ProductModels) > 0 {
		return e.findProductSensorList(req)
	}
	return e.findDevSensorList(req)
}

func (e *SysDevSensor) findDevSensorList(req *query.SysDevSensorQuery) (err error, result interface{}) {
	devQuery := query.SysDevQuery{Sn: req.Sn, UserInfo: req.UserInfo, DeptIdNot: req.DeptIdNot, DeptId: req.DeptId, OnlineStatus: req.OnlineStatus, NameSearch: req.DevName, SnSearch: req.DevSn, ProductModelNotInStr: req.ProductNot}
	// 查一堆东西
	snList := make([]string, 0)
	devList := make([]SysDev, 0)
	snDev := make(map[string]SysDev)
	if err = e.Orm.Model(&SysDev{}).Select([]string{"sn", "dept_id", "type", "name"}).Scopes(cDto.MakeCondition(devQuery.GetNeedSearch())).Scan(&devList).Error; err != nil {
		return
	}
	// tag查询 预处理
	var tagSns, tagKeys []string
	if len(req.Tag) > 0 {
		req.TagsNotNil = true
		tmpSnList := make([]string, 0)
		for _, dev := range devList {
			tmpSnList = append(tmpSnList, dev.Sn)
		}
		tagSns = make([]string, 0)
		tagKeys = make([]string, 0)
		rs := make([]map[string]interface{}, 0)
		tagsplit := strings.Split(strings.ReplaceAll(req.Tag, " ", ""), ",")
		tagsql := strings.Join(tagsplit, "\"','\"")
		tagsql = "select * from sys_dev_sensor,jsonb_array_elements((param->>'tags')::jsonb) as h where h :: VARCHAR IN ('\"" + tagsql + "\"')"
		if len(tmpSnList) > 0 {
			tagsql += " and sn in ('" + strings.Join(tmpSnList, "','") + "')"
		}
		if err = e.Orm.Raw(tagsql).Scan(&rs).Error; err != nil {
			return
		}
		if len(rs) == 0 {
			result = norecord(req.PageIndex, req.PageSize)
			return
		}
		for _, m := range rs {
			if len(m["sn"].(string)) > 0 && !utils.StrIn(m["sn"].(string), tagSns) {
				tagSns = append(tagSns, m["sn"].(string))
			}
			if len(m["key"].(string)) > 0 && !utils.StrIn(m["key"].(string), tagKeys) {
				tagKeys = append(tagKeys, m["key"].(string))
			}
		}
	}
	// 权限处理
	req.GetNeedSearch()
	querySnIn := req.SnIn
	queryKeyIn := req.KeyIn
	for i, dev := range devList {
		if tagSns != nil && !utils.StrIn(dev.Sn, tagSns) {
			continue
		}
		snList = append(snList, dev.Sn)
		snDev[dev.Sn] = devList[i]
	}
	req.SnIn = snList
	if len(querySnIn) > 0 {
		// 取交集
		for i := len(req.SnIn) - 1; i >= 0; i-- {
			if !utils.StrIn(req.SnIn[i], querySnIn) {
				req.SnIn = append(req.SnIn[:i], req.SnIn[i+1:]...)
			}
		}
		if len(req.SnIn) == 0 {
			result = norecord(req.PageIndex, req.PageSize)
			return
		}
	}
	// 变量告警状态
	alarmSnKeys := make([]string, 0)
	alarmRow, _ := env.Cache.HashGetAll(constant.HasAlarmRowCache)
	for ar := range alarmRow {
		alarmSnKeys = append(alarmSnKeys, ar[:strings.LastIndex(ar, "|")])
	}
	if req.AlarmStatus != nil {
		// sn|key拼接的in/nin
		if *req.AlarmStatus {
			req.SnKeyConcatIn = alarmSnKeys
		} else {
			if len(alarmSnKeys) > 0 {
				req.SnKeyConcatNIn = alarmSnKeys
			}
		}
	}
	if len(tagKeys) > 0 {
		if len(req.KeyIn) > 0 {
			// 取交集
			for i := len(req.KeyIn) - 1; i >= 0; i-- {
				if !utils.StrIn(req.KeyIn[i], tagKeys) {
					req.KeyIn = append(req.KeyIn[:i], req.KeyIn[i+1:]...)
				}
			}
			if len(req.KeyIn) == 0 {
				result = norecord(req.PageIndex, req.PageSize)
				return
			}
		} else {
			req.KeyIn = tagKeys
		}
	}
	if len(queryKeyIn) > 0 {
		// 取交集
		for i := len(req.KeyIn) - 1; i >= 0; i-- {
			if !utils.StrIn(req.KeyIn[i], queryKeyIn) {
				req.KeyIn = append(req.KeyIn[:i], req.KeyIn[i+1:]...)
			}
		}
		if len(req.KeyIn) == 0 {
			result = norecord(req.PageIndex, req.PageSize)
			return
		}
	}
	// 查询变量
	list := make([]SysDevSensor, 0)
	var count int64
	if req.PageIndex > 0 && req.PageSize > 0 {
		where := make([]func(db *gorm.DB) *gorm.DB, 0)
		where = append(where, cDto.MakeCondition(*req))
		if req.CustOrder != "" {
			//增加自定义排序：req.CustOrder=sn asc, label asc, update_time desc, SQL: order by sn asc, label asc, update_time desc
			ps := strings.Split(req.CustOrder, ",")
			for _, p := range ps {
				or := strings.Split(p, "=")
				where = append(where, func(db *gorm.DB) *gorm.DB {
					return db.Order(`"` + or[0] + `"` + utils.If(or[1] == "desc", " DESC NULLS LAST", " NULLS LAST").(string))
				})
			}
		} else {
			where = append(where, cDto.OrderDest("sn,key", true, req.OrderBy))
		}
		where = append(where, cDto.Paginate(req.GetPageSize(), req.GetPageIndex()))
		err = e.FindPage(&list, &count, where...)
		groupIdName, _ := utils.GroupIdName(e.Orm)
		for i := range list {
			sn := list[i].Sn
			if dev, ok := snDev[sn]; ok {
				list[i].DeptId = dev.DeptId
				list[i].DeptName = groupIdName[dev.DeptId]
				list[i].DevName = dev.Name
				list[i].DevType = dev.Type
				if timeI, ok := constant.DevOnlineCache.Get(dev.Sn); ok {
					list[i].OnlineStatus = enums.Online
					timeTime, _ := time.ParseInLocation("2006-01-02 15:04:05", timeI, time.Local)
					t := cDto.LocalTime(timeTime)
					list[i].UpdateTime = &t
				} else {
					list[i].OnlineStatus = enums.Offline
				}
			}
			fd := sn + "|" + list[i].Key
			if utils.StrIn(fd, alarmSnKeys) {
				list[i].AlarmStatus = true
			}
			// 展示取缓存
			if tmpData, ok := constant.SensorCache.Get(sn); ok && tmpData != nil {
				for k, v := range tmpData {
					if list[i].Key != k {
						continue
					}
					list[i].Data = utils.MapToBytes(map[string]interface{}{k: v})
					// 更新时间
					if uptime, ok := constant.SensorSnKey.Get(sn + "#" + k); ok {
						upTime, _ := utils.FormatStrTime(uptime)
						t := cDto.LocalTime(upTime)
						list[i].UpdateTime = &t
					}
				}
			}
		}
		if len(req.OrderBy.SortOrder) > 0 && req.OrderBy.SortField == "update_time" {
			// 矫正顺序(update_time数据库排序、与缓存更新赋值存在差异性)
			sort.Slice(list, func(i, j int) bool {
				desc := utils.If(strings.ToUpper(req.OrderBy.SortOrder) != "ASC", true, false).(bool)
				if list[i].UpdateTime == nil || list[j].UpdateTime == nil {
					return false
				}
				if desc {
					return !time.Time(*(list[i].UpdateTime)).Before(time.Time(*(list[j].UpdateTime)))
				} else {
					return time.Time(*(list[i].UpdateTime)).Before(time.Time(*(list[j].UpdateTime)))
				}
			})
		}
		result = &cDto.Page{
			Records:   list,
			Total:     count,
			PageSize:  req.GetPageSize(),
			PageIndex: req.GetPageIndex(),
			Pages:     (count + int64(req.GetPageSize()) - 1) / int64(req.GetPageSize()),
			Other:     nil,
		}
	} else {
		err = e.FindList(&list,
			cDto.MakeCondition(*req),
			cDto.OrderDest("sn,key", true, req.OrderBy),
		)
		groupIdName, _ := utils.GroupIdName(e.Orm)
		for i := range list {
			sn := list[i].Sn
			if dev, ok := snDev[sn]; ok {
				list[i].DeptId = dev.DeptId
				list[i].DeptName = groupIdName[dev.DeptId]
				list[i].DevName = dev.Name
				list[i].DevType = dev.Type
				if timeI, ok := constant.DevOnlineCache.Get(dev.Sn); ok {
					list[i].OnlineStatus = enums.Online
					timeTime, _ := time.ParseInLocation("2006-01-02 15:04:05", timeI, time.Local)
					t := cDto.LocalTime(timeTime)
					list[i].UpdateTime = &t
				} else {
					list[i].OnlineStatus = enums.Offline
				}
			}
			fd := sn + "|" + list[i].Key
			if utils.StrIn(fd, alarmSnKeys) {
				list[i].AlarmStatus = true
			}
			// 展示取缓存
			if tmpData, ok := constant.SensorCache.Get(sn); ok && tmpData != nil {
				for k, v := range tmpData {
					if list[i].Key != k {
						continue
					}
					list[i].Data = utils.MapToBytes(map[string]interface{}{k: v})
					// 更新时间
					if uptime, ok := constant.SensorSnKey.Get(sn + "#" + k); ok {
						upTime, _ := utils.FormatStrTime(uptime)
						t := cDto.LocalTime(upTime)
						list[i].UpdateTime = &t
					}
				}
			}
		}
		if len(req.OrderBy.SortOrder) > 0 && req.OrderBy.SortField == "update_time" {
			// 矫正顺序(update_time数据库排序、与缓存更新赋值存在差异性)
			sort.Slice(list, func(i, j int) bool {
				desc := utils.If(strings.ToUpper(req.OrderBy.SortOrder) != "ASC", true, false).(bool)
				if list[i].UpdateTime == nil || list[j].UpdateTime == nil {
					return false
				}
				if desc {
					return !time.Time(*(list[i].UpdateTime)).Before(time.Time(*(list[j].UpdateTime)))
				} else {
					return time.Time(*(list[i].UpdateTime)).Before(time.Time(*(list[j].UpdateTime)))
				}
			})
		}
		result = list
	}
	return
}

func (e *SysDevSensor) findProductSensorList(req *query.SysDevSensorQuery) (err error, result interface{}) {
	productQuery := query.SysProductQuery{UserInfo: req.UserInfo, DeptIdNot: req.DeptIdNot, DeptId: req.DeptId, ProductModel: req.ProductModel, ProductModels: req.ProductModels}
	// 查一堆东西
	productModel := &SysDevProduct{}
	productModel.Orm = e.Orm
	pmList := make([]string, 0)
	productList := make([]SysDevProduct, 0)
	mdProd := make(map[string]SysDevProduct)
	if err = productModel.FindList(&productList, cDto.MakeCondition(productQuery.GetNeedSearch())); err != nil {
		return
	}
	// tag查询 预处理
	var tagPms, tagKeys []string
	if len(req.Tag) > 0 {
		req.TagsNotNil = true
		tmpModelList := make([]string, 0)
		for _, prod := range productList {
			tmpModelList = append(tmpModelList, prod.ProductModel)
		}
		tagPms = make([]string, 0)
		tagKeys = make([]string, 0)
		rs := make([]map[string]interface{}, 0)
		tagsplit := strings.Split(strings.ReplaceAll(req.Tag, " ", ""), ",")
		tagsql := strings.Join(tagsplit, "\"','\"")
		tagsql = "select * from sys_dev_sensor,jsonb_array_elements((param->>'tags')::jsonb) as h where h :: VARCHAR IN ('\"" + tagsql + "\"')"
		if len(tmpModelList) > 0 {
			tagsql += " and product_model in ('" + strings.Join(tmpModelList, "','") + "')"
		}
		if err = e.Orm.Raw(tagsql).Scan(&rs).Error; err != nil {
			return
		}
		if len(rs) == 0 {
			result = norecord(req.PageIndex, req.PageSize)
			return
		}
		for _, m := range rs {
			if len(m["product_model"].(string)) > 0 && !utils.StrIn(m["product_model"].(string), tagPms) {
				tagPms = append(tagPms, m["product_model"].(string))
			}
			if len(m["key"].(string)) > 0 && !utils.StrIn(m["key"].(string), tagKeys) {
				tagKeys = append(tagKeys, m["key"].(string))
			}
		}
	}
	// 权限处理
	req.GetNeedSearch()
	queryModelIn := req.ProductModelIn
	queryKeyIn := req.KeyIn
	for i, devProduct := range productList {
		if tagPms != nil && !utils.StrIn(devProduct.ProductModel, tagPms) {
			continue
		}
		pmList = append(pmList, devProduct.ProductModel)
		mdProd[devProduct.ProductModel] = productList[i]
	}
	req.ProductModelIn = pmList
	if len(queryModelIn) > 0 {
		// 取交集
		for i := len(req.ProductModelIn) - 1; i >= 0; i-- {
			if !utils.StrIn(req.ProductModelIn[i], queryModelIn) {
				req.ProductModelIn = append(req.ProductModelIn[:i], req.ProductModelIn[i+1:]...)
			}
		}
		if len(req.ProductModelIn) == 0 {
			result = norecord(req.PageIndex, req.PageSize)
			return
		}
	}
	if len(tagKeys) > 0 {
		if len(req.KeyIn) > 0 {
			// 取交集
			for i := len(req.KeyIn) - 1; i >= 0; i-- {
				if !utils.StrIn(req.KeyIn[i], tagKeys) {
					req.KeyIn = append(req.KeyIn[:i], req.KeyIn[i+1:]...)
				}
			}
			if len(req.KeyIn) == 0 {
				result = norecord(req.PageIndex, req.PageSize)
				return
			}
		} else {
			req.KeyIn = tagKeys
		}
	}
	if len(queryKeyIn) > 0 {
		// 取交集
		for i := len(req.KeyIn) - 1; i >= 0; i-- {
			if !utils.StrIn(req.KeyIn[i], queryKeyIn) {
				req.KeyIn = append(req.KeyIn[:i], req.KeyIn[i+1:]...)
			}
		}
		if len(req.KeyIn) == 0 {
			result = norecord(req.PageIndex, req.PageSize)
			return
		}
	}
	// 查询变量
	list := make([]SysDevSensor, 0)
	var count int64
	if req.PageIndex > 0 && req.PageSize > 0 {
		where := make([]func(db *gorm.DB) *gorm.DB, 0)
		where = append(where, cDto.MakeCondition(*req))
		where = append(where, cDto.OrderDest("product_model,key", true, req.OrderBy))
		where = append(where, cDto.Paginate(req.GetPageSize(), req.GetPageIndex()))
		err = e.FindPage(&list, &count, where...)
		groupIdName, _ := utils.GroupIdName(e.Orm)
		for i := range list {
			pm := list[i].ProductModel
			if devProduct, ok := mdProd[pm]; ok {
				list[i].DeptId = devProduct.DeptId
				list[i].DeptName = groupIdName[devProduct.DeptId]
				list[i].ProductName = devProduct.Name
				list[i].DevType = devProduct.DeviceType
			}
		}
		result = &cDto.Page{
			Records:   list,
			Total:     count,
			PageSize:  req.GetPageSize(),
			PageIndex: req.GetPageIndex(),
			Pages:     (count + int64(req.GetPageSize()) - 1) / int64(req.GetPageSize()),
			Other:     nil,
		}
	} else {
		err = e.FindList(&list,
			cDto.MakeCondition(*req),
			cDto.OrderDest("product_model,key", true, req.OrderBy),
		)
		groupIdName, _ := utils.GroupIdName(e.Orm)
		for i := range list {
			pm := list[i].ProductModel
			if devProduct, ok := mdProd[pm]; ok {
				list[i].DeptId = devProduct.DeptId
				list[i].DeptName = groupIdName[devProduct.DeptId]
				list[i].ProductName = devProduct.Name
				list[i].DevType = devProduct.DeviceType
			}
		}
		result = list
	}
	return
}

// sensor/get、set调用更新
func (e *SysDevSensor) UpdateDevSensor(sn string, data map[string]interface{}, override ...bool) error {
	t1 := time.Now()
	cacheResult := utils.BytesToMap(utils.MapToBytes(data))
	if len(override) > 0 && override[0] {
		// 	覆盖
	} else {
		// 合并
		if tmp, ok := constant.SensorCache.Get(sn); ok && tmp != nil {
			utils.DeepMerge(tmp, cacheResult)
		}
	}
	// 更新sensor表data
	scan := map[string]interface{}{}
	sql := "UPDATE sys_dev_sensor SET data = tmp.data FROM (VALUES %s) as tmp (sn, key, data) where sys_dev_sensor.sn = tmp.sn and sys_dev_sensor.key = tmp.key"
	valueSql := make([]string, 0)
	for k, v := range cacheResult {
		valueSql = append(valueSql, "('"+sn+"','"+k+"', '"+string(utils.MapToBytes(map[string]interface{}{k: v}))+"'::jsonb)")
	}
	sql = fmt.Sprintf(sql, strings.Join(valueSql, ","))
	if err := env.Db.Raw(sql).Scan(&scan).Error; err != nil {
		logger.Error(err)
		return err
	}
	// 更新sensor缓存
	constant.SensorCache.Set(sn, cacheResult)
	logger.Infof("sensor/get(set)数据更新. 耗时%v", time.Since(t1).Round(time.Millisecond))
	return nil
}

func (e *SysDevSensor) FindSchema(req *query.SysDevSensorQuery) (err error, schemaInfo map[string]interface{}) {
	schemaInfo = make(map[string]interface{})
	devType := enums.DevTypeOfDevice
	configQuery := query.SysDevConfigQuery{}
	funcQuery := query.SysDevFuncQuery{}
	if len(req.Sn) > 0 {
		devModel := &SysDev{}
		devModel.Orm = env.Db
		if err = devModel.FindOne(devModel, cDto.MakeCondition(query.SysDevQuery{Sn: req.Sn})); err != nil {
			return
		}
		schemaInfo["sn"] = devModel.Sn
		devType = devModel.Type
		configQuery.Sn = devModel.Sn
		funcQuery.Sn = devModel.Sn
	} else if len(req.ProductModel) > 0 {
		pdModel := &SysDevProduct{}
		pdModel.Orm = env.Db
		if err = pdModel.FindOne(pdModel, cDto.MakeCondition(query.SysProductQuery{ProductModel: req.ProductModel})); err != nil {
			return
		}
		schemaInfo["productModel"] = pdModel.ProductModel
		devType = pdModel.DeviceType
		configQuery.ProductModel = pdModel.ProductModel
		funcQuery.ProductModel = pdModel.ProductModel
	} else {
		return errors.New("查询参数异常"), schemaInfo
	}
	// 查询config
	configM := &SysDevConfig{}
	configM.Orm = env.Db
	if err = configM.FindOne(configM, cDto.MakeCondition(configQuery)); err != nil {
		return
	}
	// 查询func
	funcM := &SysDevFunc{}
	funcM.Orm = env.Db
	if err = funcM.FindOne(funcM, cDto.MakeCondition(funcQuery)); err != nil {
		return
	}
	// 模板展示
	schemaTemplate := utils.If(devType == enums.DevTypeOfDevice, constant.DevSchemaTemplate, constant.GatewaySchemaTemplate).(string)
	// 产品basic
	if schemaInfo["productModel"] != nil {
		tmpSchemaJson := utils.BytesToMap([]byte(schemaTemplate))
		productSchemaBasic := utils.BytesToMap([]byte(constant.ProductBasicSchemaTemplate))
		tmpSchemaJson["properties"].(map[string]interface{})["props"].(map[string]interface{})["properties"].(map[string]interface{})["basic"] = productSchemaBasic
		schemaTemplate = string(utils.MapToBytes(tmpSchemaJson))
	}
	// 查询sensor表
	sensors := make([]SysDevSensor, 0)
	if err = e.FindList(&sensors, cDto.MakeCondition(req.GetNeedSearch())); err != nil {
		return
	}
	// 没有变量
	if len(sensors) == 0 {
		schemaInfo["schema"] = repSchema(req, configM, funcM, utils.BytesToMap([]byte(schemaTemplate)))
		return
	}
	// 组合各变量的schema
	tmpSchemaJson := utils.BytesToMap([]byte(schemaTemplate))
	sensorProp := tmpSchemaJson["properties"].(map[string]interface{})["sensor"].(map[string]interface{})["properties"].(map[string]interface{})
	for _, sensor := range sensors {
		utils.DeepMerge(utils.BytesToMap(sensor.Schema), sensorProp)
	}
	schemaInfo["schema"] = repSchema(req, configM, funcM, tmpSchemaJson)
	return
}

func repSchema(req *query.SysDevSensorQuery, configM *SysDevConfig, funcM *SysDevFunc, tmpSchema map[string]interface{}) interface{} {
	// 替换config
	if configM != nil && configM.Data != nil {
		// 先得到config本身schema
		configMap := utils.BytesToMap(configM.Data)["config"].(map[string]interface{})
		configSchema := utils.GetObjectSchema(configMap)["properties"].(map[string]interface{})
		if configM.Schema != nil {
			// 再将定义的schema与之合并(其他更新config的地方就不用维护schema了)
			configSchemaMap := utils.BytesToMap(configM.Schema)["config"].(map[string]interface{})["properties"].(map[string]interface{})
			utils.DeepMerge(configSchemaMap, configSchema)
		}
		tmpSchema["properties"].(map[string]interface{})["config"].(map[string]interface{})["properties"] = configSchema
	}
	// 替换func
	if funcM != nil && funcM.Data != nil {
		// 先得到func本身schema
		funcMap := utils.BytesToMap(funcM.Data)["func"].(map[string]interface{})
		funcSchema := utils.GetObjectSchema(funcMap)["properties"].(map[string]interface{})
		if funcM.Schema != nil {
			// 再将定义的schema与之合并(其他更新func的地方就不用维护schema了)
			funcSchemaMap := utils.BytesToMap(funcM.Schema)["func"].(map[string]interface{})["properties"].(map[string]interface{})
			utils.DeepMerge(funcSchemaMap, funcSchema)
		}
		tmpSchema["properties"].(map[string]interface{})["func"].(map[string]interface{})["properties"] = funcSchema
	}
	// 路径处理
	if len(req.SchemaPath) > 0 {
		paths := strings.Split(req.SchemaPath, ".")
		return utils.GetFieldFromJson(paths, tmpSchema)
	} else {
		return tmpSchema
	}
}

func norecord(pageIndex, pageSize int) interface{} {
	list := make([]map[string]interface{}, 0)
	if pageIndex > 0 && pageSize > 0 {
		return &cDto.Page{
			Records:   list,
			Total:     0,
			PageSize:  pageSize,
			PageIndex: pageIndex,
			Pages:     (0 + int64(pageSize) - 1) / int64(pageSize),
			Other:     nil,
		}
	} else {
		return list
	}
}

func (e *SysDevSensor) ImportUpsert(sn string, vars []SysDevSensor) (err error) {
	if len(vars) == 0 {
		return
	}
	addList := make([]SysDevSensor, 0)
	modList := make([]SysDevSensor, 0)
	varConfigMap := make(map[string]interface{})
	now := cDto.LocalTime(time.Now())
	for i, sen := range vars {
		key := sen.Key
		data := utils.BytesToMap(sen.Data)
		config := utils.BytesToMap(sen.Config)
		utils.DeepMerge(utils.GetObjectConfig(data), config)
		vars[i].Config = utils.MapToBytes(config)
		dataSchema := utils.GetObjectSchema(data)["properties"].(map[string]interface{})
		vars[i].Schema = utils.MapToBytes(dataSchema)
		var tmpMap map[string]interface{}
		if value, ok := constant.SensorCache.Get(sn); !ok {
			vars[i].Id = utils.SimpleUUID()
			vars[i].CreateTime = &now
			addList = append(addList, vars[i])
			tmpMap = data
		} else {
			tmpMap = value
			if tv, okk := tmpMap[key]; !okk {
				vars[i].Id = utils.SimpleUUID()
				vars[i].CreateTime = &now
				addList = append(addList, vars[i])
			} else {
				tmpKeyData := map[string]interface{}{key: tv}
				utils.DeepMerge(tmpKeyData, data)
				vars[i].Data = utils.MapToBytes(data)
				utils.DeepMerge(utils.GetObjectConfig(tmpKeyData), config)
				vars[i].Config = utils.MapToBytes(config)
				tmpSchema := utils.GetObjectSchema(tmpKeyData)["properties"].(map[string]interface{})
				utils.DeepMerge(tmpSchema, dataSchema)
				vars[i].Schema = utils.MapToBytes(dataSchema)
				modList = append(modList, vars[i])
			}
			utils.DeepMerge(data, tmpMap)
		}
		constant.SensorCache.Set(sn, tmpMap)
		utils.DeepMerge(config, varConfigMap)
	}
	if len(addList) > 0 {
		if err = e.Add(&addList); err != nil {
			return
		}
	}
	scan := map[string]interface{}{}
	if len(modList) > 0 {
		sql := "UPDATE sys_dev_sensor SET label = tmp.label, type = tmp.type, data = tmp.data, config = tmp.config, param = tmp.param, schema = tmp.schema FROM (VALUES %s) as tmp (sn, key, label, type, data, config, param, schema) where sys_dev_sensor.sn = tmp.sn and sys_dev_sensor.key = tmp.key"
		valueSql := make([]string, 0)
		for _, s := range modList {
			param := string(s.Param)
			if len(param) == 0 {
				param = "{}"
			}
			valueSql = append(valueSql, "('"+s.Sn+"','"+s.Key+"', '"+s.Label+"','"+s.Type+"','"+string(s.Data)+"'::jsonb,'"+string(s.Config)+"'::jsonb,'"+param+"'::jsonb,'"+string(s.Schema)+"'::jsonb)")
		}
		sql = fmt.Sprintf(sql, strings.Join(valueSql, ","))
		if err = e.Orm.Raw(sql).Scan(&scan).Error; err != nil {
			return
		}
	}
	if len(varConfigMap) > 0 {
		path := "{config,sensor}"
		dataStr := string(utils.MapToBytes(varConfigMap))
		sql := "update sys_dev_config set data = jsonb_set(data::jsonb, '" + path + "', jsonb_update(data #> '" + path + "', '" + dataStr + "' :: jsonb ) )  where dev_sn = '" + sn + "'"
		if err = e.Orm.Raw(sql).Scan(&scan).Error; err != nil {
			return
		}
	}
	return
}

type ProtoSensor struct {
	SysProtocol SysProtocol
	Vars        []SysDevSensor
}

// 按协议分类的变量
func (e *SysDevSensor) FindProtocolSensorList(req *query.SysDevSensorQuery) (sortedProtoSensor []ProtoSensor, err error) {
	vars := make([]SysDevSensor, 0)
	var result interface{}
	if err, result = e.FindSensorList(req); err != nil {
		return
	} else {
		vars = result.([]SysDevSensor)
	}
	if len(vars) == 0 {
		return
	}
	protoMap := (&SysProtocol{}).FindProtocolMap(e.Orm)
	// 增加一个未知协议的项
	protoMap[""] = SysProtocol{
		Id:    100000,
		Pid:   0,
		Label: "未知协议",
	}
	mapProtoSensor := make(map[string]*ProtoSensor)
	if len(req.ProductModel) > 0 || len(req.ProductModels) > 0 {
		// TODO 产品暂无协议字段 and 没有导出操作
	} else {
		sns := make([]string, 0)
		for _, sensor := range vars {
			if len(sensor.Sn) == 0 || utils.StrIn(sensor.Sn, sns) {
				continue
			}
			sns = append(sns, sensor.Sn)
		}
		devM := SysDev{}
		devM.Orm = e.Orm
		devs := make([]SysDev, 0)
		if err = devM.FindList(&devs, cDto.MakeCondition(query.SysDevQuery{SnIn: sns})); err != nil {
			return
		}
		devProtoMap := make(map[string]string)
		for _, dev := range devs {
			devProtoMap[dev.Sn] = dev.Protocol
		}
		for i, sensor := range vars {
			theProto, ok := devProtoMap[sensor.Sn]
			if !ok {
				// 变量对应的设备不存在 ?
				continue
			}
			var ps *ProtoSensor
			if ps, ok = mapProtoSensor[theProto]; !ok {
				mapProtoSensor[theProto] = &ProtoSensor{
					SysProtocol: protoMap[theProto],
					Vars:        []SysDevSensor{vars[i]},
				}
			} else {
				ps.Vars = append(ps.Vars, vars[i])
			}
		}
	}
	protoSensors := make([]ProtoSensor, 0)
	for _, p := range mapProtoSensor {
		protoSensors = append(protoSensors, *p)
	}
	sortedProtoVars := SortProtoSensor(protoSensors)
	sort.Sort(sortedProtoVars)
	sortedProtoSensor = sortedProtoVars
	return
}

type SortProtoSensor []ProtoSensor

func (p SortProtoSensor) Len() int {
	return len(p)
}

func (p SortProtoSensor) Less(i, j int) bool {
	pi := p[i].SysProtocol
	pj := p[j].SysProtocol
	// 升序
	return pi.Id < pj.Id
}

func (p SortProtoSensor) Swap(i, j int) {
	p[i], p[j] = p[j], p[i]
}
